package me.mingshan.leetcode;

/**
 * https://leetcode.cn/problems/minimum-sum-of-mountain-triplets-i/description/
 * <p>
 * 给你一个下标从 0 开始的整数数组 nums 。
 * <p>
 * 如果下标三元组 (i, j, k) 满足下述全部条件，则认为它是一个 山形三元组 ：
 * <p>
 * i < j < k
 * nums[i] < nums[j] 且 nums[k] < nums[j]
 * 请你找出 nums 中 元素和最小 的山形三元组，并返回其 元素和 。如果不存在满足条件的三元组，返回 -1 。
 * <p>
 * <p>
 * <p>
 * 示例 1：
 * <p>
 * 输入：nums = [8,6,1,5,3]
 * 输出：9
 * 解释：三元组 (2, 3, 4) 是一个元素和等于 9 的山形三元组，因为：
 * - 2 < 3 < 4
 * - nums[2] < nums[3] 且 nums[4] < nums[3]
 * 这个三元组的元素和等于 nums[2] + nums[3] + nums[4] = 9 。可以证明不存在元素和小于 9 的山形三元组。
 * 示例 2：
 * <p>
 * 输入：nums = [5,4,8,7,10,2]
 * 输出：13
 * 解释：三元组 (1, 3, 5) 是一个元素和等于 13 的山形三元组，因为：
 * - 1 < 3 < 5
 * - nums[1] < nums[3] 且 nums[5] < nums[3]
 * 这个三元组的元素和等于 nums[1] + nums[3] + nums[5] = 13 。可以证明不存在元素和小于 13 的山形三元组。
 * 示例 3：
 * <p>
 * 输入：nums = [6,5,4,3,4,5]
 * 输出：-1
 * 解释：可以证明 nums 中不存在山形三元组。
 *
 * @author hanjuntao
 * @date 2025/7/19 0019
 */
public class L_2908_minimum_sum_of_mountain_triplets_i {

    public static void main(String[] args) {
        int[] nums = new int[]{8, 6, 1, 5, 3};
        System.out.println(minimumSum3(nums));
        // 9

        int[] nums2 = new int[]{5, 4, 8, 7, 10, 2};
        System.out.println(minimumSum3(nums2));
        // 13
    }

    /**
     * 思路：
     * <p>
     * 满足以下条件：
     * 1. i < j < k
     * 2. nums[i] < nums[j] 且 nums[k] < nums[j]
     *
     * @param nums
     * @return
     */
    public static int minimumSum(int[] nums) {
        int minSum = Integer.MAX_VALUE;

        // 暴力
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                for (int k = j + 1; k < nums.length; k++) {
                    if (nums[i] < nums[j] && nums[k] < nums[j]) {
                        int curr = nums[i] + nums[j] + nums[k];
                        minSum = Math.min(minSum, curr);
                    }
                }
            }
        }

        return minSum == Integer.MAX_VALUE ? -1 : minSum;
    }

    /**
     * 思路：
     * 找到一个基准数（山峰），找到左边比基准小的最小值；找到右边比基准小的最小值
     * <p>
     * 如果两个都能找到，则更新结果.
     * <p>
     * 时间复杂度：O(n^2)
     *
     * @param nums
     * @return
     */
    public static int minimumSum2(int[] nums) {
        int minSum = Integer.MAX_VALUE;

        for (int i = 1; i < nums.length - 1; i++) {
            int base = nums[i];

            // 找到左边的数比它小
            int left = i - 1;
            int leftMin = Integer.MAX_VALUE;
            while (left >= 0) {
                if (nums[left] < base) {
                    leftMin = Math.min(leftMin, nums[left]);
                }
                left--;
            }

            int right = i + 1;
            int rightMin = Integer.MAX_VALUE;
            while (right < nums.length) {
                if (nums[right] < base) {
                    rightMin = Math.min(rightMin, nums[right]);
                }
                right++;
            }

            if (leftMin != Integer.MAX_VALUE && rightMin != Integer.MAX_VALUE) {
                minSum = Math.min(minSum, rightMin + leftMin + base);
            }
        }

        return minSum == Integer.MAX_VALUE ? -1 : minSum;
    }


    /**
     * 思路：
     * <p>
     * 从O(N^2)算法可知，对于任意一个值，我们只要找到左边比它小且最小的数、右边比它小的数且最小的数，就可以计算出这个三元组的和。
     * <p>
     * 可以先提前把这个数提前计算起来，当我们遍历到这个数时，就可以直接从计算好的数组中获取到这个数左边比它小的数、右边比它小的数。
     * <p>
     * 时间复杂度将降下来了 。
     * <p>
     * 时间复杂度：O(n)
     *
     * @param nums
     * @return
     */
    public static int minimumSum3(int[] nums) {
        int[] leftMin = new int[nums.length];
        int[] rightMin = new int[nums.length];

        int min = nums[0];
        // 从左往后遍历
        for (int i = 1; i < nums.length; i++) {
            leftMin[i] = min;
            // 更新最小值
            min = Math.min(min, nums[i]);
        }

        // 从右往左遍历
        min = nums[nums.length - 1];
        for (int i = nums.length - 2; i >= 0; i--) {
            rightMin[i] = min;
            min = Math.min(min, nums[i]);
        }

        int minSum = Integer.MAX_VALUE;

        for (int i = 1; i < nums.length - 1; i++) {
            if (nums[i] > leftMin[i] && nums[i] > rightMin[i]) {
                minSum = Math.min(minSum, leftMin[i] + rightMin[i] + nums[i]);
            }
        }

        return minSum == Integer.MAX_VALUE ? -1 : minSum;
    }
}
